﻿/* This code is restricted under LGPL license */
using NHibernate.Criterion;

namespace infoMECHANICA.NHibernateORM.CriteriaBuilder
{
    /// <summary>
    /// Generic NHibernate detached criteria what means criteria, which has been projected
    /// to expose property of specific, generic type T
    /// </summary>
    /// <typeparam name="T">Type of property, projected by detached criteria</typeparam>
    public class GenericSubqueryProjection<T> : PropertyNameHolder
    {
        private DetachedCriteria projectedCriteria;
        private DetachedCriteria savedCriteria;
        private SubqueryProjectionTypes savedSubqueryProjectionType;

        /// <summary>
        /// Saved type of subquery projection (in case of conversion to GenericSubqueryProjection[T])
        /// </summary>
        protected SubqueryProjectionTypes SavedSubqueryProjectionType
        {
            get { return savedSubqueryProjectionType; }
            set { savedSubqueryProjectionType = value; }
        }

        /// <summary>
        /// Saved criteria passed to constructor (in case of conversion to GenericSubqueryProjection[T])
        /// </summary>
        protected DetachedCriteria SavedCriteria
        {
            get { return savedCriteria; }
            set { savedCriteria = value; }
        }

        /// <summary>
        /// NHibernate detached criteria
        /// </summary>
        public DetachedCriteria ProjectedCriteria
        {
            get { return projectedCriteria; }
            set { projectedCriteria = value; }
        }
        
        /// <summary>
        /// Constructor, sets ProjectedCriteria
        /// </summary>
        /// <param name="criteria">NHibernate detached criteria, which will be projected</param>
        /// <param name="propertyName">Name of property which will be projected</param>
        public GenericSubqueryProjection(DetachedCriteria criteria, string propertyName, SubqueryProjectionTypes subqueryProjectionType) : base(propertyName)
        {
            this.SavedCriteria = criteria;
            this.SavedSubqueryProjectionType = subqueryProjectionType;
            
            switch (subqueryProjectionType)
            {
                case SubqueryProjectionTypes.Property:
                    criteria.SetProjection(Projections.Property(propertyName));
                    break;
                case SubqueryProjectionTypes.Max:
                    criteria.SetProjection(Projections.Max(propertyName));
                    break;
                case SubqueryProjectionTypes.Min:
                    criteria.SetProjection(Projections.Min(propertyName));
                    break;
                case SubqueryProjectionTypes.Sum:
                    criteria.SetProjection(Projections.Sum(propertyName));
                    break;
                case SubqueryProjectionTypes.Avg:
                    criteria.SetProjection(Projections.Avg(propertyName));
                    break;
            }
            
            this.ProjectedCriteria = criteria;
        }

        public GenericSubqueryProjection<object> ToObjectSubqueryProjection()
        {                        
            return new GenericSubqueryProjection<object>(this.SavedCriteria, 
                this.PropertyName, this.SavedSubqueryProjectionType);
        }
    }
}
